package com.leyou.search.service.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.leyou.common.exception.LyException;
import com.leyou.item.client.ItemClient;
import com.leyou.search.dto.SearchParamDTO;
import com.leyou.search.entity.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.service.SearchService;
import com.leyou.starter.elastic.entity.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private GoodsRepository goodsRepository;
    @Autowired
    private ItemClient itemClient;

    @Override
    public Mono<List<String>> suggest(String key) {

       return this.goodsRepository.suggestBySingleField("suggestion",key);

    }
    //获取商品+分页
    @Override
    public Mono<PageInfo<Goods>> queryPage(SearchParamDTO param) {

        QueryBuilder queryBuilder = getQueryBuilder(param);

        //构建查询条件的工厂对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //封装查询条件
        sourceBuilder.query(queryBuilder);

        //封装分页条件
        sourceBuilder.from(param.getFrom());
        sourceBuilder.size(param.getSize());

        return  this.goodsRepository.queryBySourceBuilderForPageHighlight(sourceBuilder);

    }

     //提取获取QueryBuilder方法(封装查询条件)
    private QueryBuilder getQueryBuilder(SearchParamDTO param) {

        //Request URL: http://www.leyou.com/search.html?key=shouji
        String key = param.getKey();

        if(StringUtils.isEmpty(key)){

            throw new LyException(400,"没有输入查询参数");
        }

        //创建Boolean查询
       BoolQueryBuilder queryBuilder =  QueryBuilders.boolQuery();

        queryBuilder.must(QueryBuilders.matchQuery("title", key).operator(Operator.AND));


        Map<String, String> filters = param.getFilters();
        //不为空，表示要进行过滤
        if (!CollectionUtils.isEmpty(filters)) {

            filters.entrySet().forEach(entry -> {
                String specName = entry.getKey();

                String specValue = entry.getValue();

                if ("品牌".equals(specName)) {
                    specName = "brandId";
                    queryBuilder.filter(QueryBuilders.termQuery(specName, specValue));

                } else if ("分类".equals(specName)) {
                    specName = "categoryId";
                    queryBuilder.filter(QueryBuilders.termQuery(specName, specValue));
                } else {
                    //构建其他过滤规格参数的过滤条件
                    BoolQueryBuilder filterBool = QueryBuilders.boolQuery();

                    filterBool.must(QueryBuilders.termQuery("specs.name",specName));
                    filterBool.must(QueryBuilders.termQuery("specs.value",specValue));

                    QueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("specs", filterBool, ScoreMode.None);

                    queryBuilder.filter(nestedQueryBuilder);

                }
            });
        }
        return queryBuilder;
    }


    //分类+品牌+规格参数 聚合查询(TODO 要根据KQL语句写!!!!)
    @Override
    public Mono<Map<String, List<?>>> filterQuery(SearchParamDTO searchParamDTO) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //添加查询条件
        sourceBuilder.query(getQueryBuilder(searchParamDTO));
        String brandAgg="brandAgg";
        String categoryAgg="categoryAgg";
        //添加品牌聚合
        sourceBuilder.aggregation(AggregationBuilders.terms(brandAgg).field("brandId").size(100));
        //添加分类聚合
        sourceBuilder.aggregation(AggregationBuilders.terms(categoryAgg).field("categoryId").size(100));

        //条件规格参数聚合
        sourceBuilder.aggregation(
                //nested类型聚合
                AggregationBuilders.nested("specAgg", "specs")
                        .subAggregation(
                        //name聚合
                        AggregationBuilders.terms("nameAgg").field("specs.name").size(100)
                         .subAggregation(
                         //value聚合
                          AggregationBuilders.terms("valueAgg").field("specs.value").size(100))
                        )
        );

        //查询聚合,把结果返回Mono<Aggregation>转换为 Mono<Map<String, List<?>>>
        return  this.goodsRepository.aggregationBySourceBuilder(sourceBuilder).map(
                aggregations ->{
                    Map<String, List<?>> result = new LinkedHashMap<>();
                    Terms brandAggId = aggregations.get(brandAgg);

                    List<Long> brandIds = brandAggId.getBuckets()
                            .stream()
                            .map(brandBucket -> brandBucket.getKeyAsNumber().longValue())
                            .collect(Collectors.toList());

                    Terms categoryAggId = aggregations.get(categoryAgg);

                    List<Long> categoryIds = categoryAggId.getBuckets()
                            .stream()
                            .map(categoryBucket -> ((Terms.Bucket) categoryBucket).getKeyAsNumber().longValue())
                            .collect(Collectors.toList());

                    //这个添加顺序有没有影响呢??
                    if(!CollectionUtils.isEmpty(categoryIds)){

                     result.put("分类",this.itemClient.listCategoryByIds(categoryIds));
                    }

                    if(!CollectionUtils.isEmpty(brandIds)){

                        result.put("品牌",this.itemClient.queryBrandByIds(brandIds));
                    }

                    //获取nested聚合
                    Nested specAgg = aggregations.get("specAgg");

                    //获取nested聚合下的所有的聚合
                    Terms nameAgg = specAgg.getAggregations().get("nameAgg");

                    nameAgg.getBuckets().forEach(nameBucket -> {
                        String specName = nameBucket.getKeyAsString();

                        //获取nameBucket的所有子聚合
                        Terms valueAgg = nameBucket.getAggregations().get("valueAgg");
                        //获取nameBucket的子聚合中的所有的子内容
                        List<String> specValueList = valueAgg.getBuckets().stream().map(valueBucket -> ((Terms.Bucket) valueBucket).getKeyAsString()).collect(Collectors.toList());

                        result.put(specName, specValueList);

                    });
                    return   result;
                }
        );
    }
}
